﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml;

namespace ProjectFinal {
	//Media types
	public enum MediaType { adultBook, childBook, DVD, Video };

	public abstract class Item {
		protected String mediaID;		// ID code for the Item
		public string ID { get { return mediaID; } }
		public void setID(string id) { mediaID = id; }

		protected String title;		// Title of the Item
		public string Title {
			get { return title; }
		}
		public void setTitle(string t) { title = t; }

		protected Boolean checkedOut;
		public bool CheckedOut {		// TRUE if Item is checked out
			get { return checkedOut; }
		}

		protected Date dueDate;
		public Date Duedate {		//Date the Item is due back
			get { return dueDate; }
		}

		protected int patronID;		//If a Patron has this item checked out, this is their ID#
		public string PatronID {		//Get the Patron Name
			get { return patronID > 0 ? BusinessRules.getPatronByID(patronID).Name : ""; }  //Yeah, a bit anti-OOP, I know...
		}
		public int getPatron() { return patronID; }		//Get Patron's actual ID

		protected MediaType type;		//Media type
		public string Type {	//Return Media type as a User-friendly String
			get {
				switch(type) {
					case MediaType.adultBook:
						return "Adult Book";
					case MediaType.childBook:
						return "Child Book";
					default:
						return type.ToString();
				}
			}
		}
		public MediaType getType() { return type; }  //Return actual MediaType

		//Create an instance of the Item
		//Param:		Title, ID Code, Item type
		public Item(string title, string ID, MediaType type) {
			this.title = title;
			ID = ID.Replace(' ', '_');	//Replace spaces with underscores
			if(ID.Length > 8)					//Truncate after 8 characters (this is controlled elsewhere as well)
				ID = ID.Substring(0, 8);
			this.mediaID = ID;
			this.type = type;
			checkedOut = false;
		}//Item(string, string, MediaType)

		public Item() { }

		//Is this book overdue?
		//param:	Today's date
		//post:		Returns TRUE if book's due date is before today
		public bool isOverdue(Date today) { return today > this.dueDate; }

		//Mark item as Checked Out
		//Param:		Patron ID that's checking out the item, Due date
		//Post:		Item is marked as checked out and due date is recorded
		//					Returns True if checkout is successfully, False otherwise
		//Note:		Duedate is not calculated herein, it is assumed that it's correct
		public Boolean checkOut(int p, Date due) {
			if(checkedOut)
				return false;//An Item that's checked out cannot be re-checked out

			patronID = p;
			checkedOut = true;
			dueDate = due;
			return true;
		}//bool checkOut(int, Date)

		//Mark item as checked in
		//Post:	Item is checked in and no Patron ID is connected to it anymore
		public void checkIn() {
			patronID = 0;//all ids are positive ints
			checkedOut = false;
			dueDate = null;
		}

		//Return TRUE if Item is checked in
		public Boolean isCheckedIn() { return !checkedOut; }

		//Write Item to an XML file
		//Param:	XML Document, XML Node to append data
		abstract public XmlNode writeThyself(XmlDocument xmlDoc, XmlNode node);
	}//class Item

	public class Book : Item {
		private String author;	//Book's author
		public string Author {
			get { return author; }
			set { author = (value == null || value.Trim().Equals("")) ? author : value; } // Don't allow a blank author
		}

		//Create a Book object
		//Param:	Title, Author, ID Code, Type of book
		public Book(String title, String author, String bookID, MediaType type)
			: base(title, bookID, type) {
			this.author = author;
		}//Book(string, string, string, MediaType)

		//Create a Book based on data in an XML node
		//Param:	node in which to find data
		public Book(XmlNode node)
			: base() {
			this.title = node.InnerText;
			this.author = node.Attributes["author"].Value.ToString();
			this.mediaID = node.Attributes["id"].Value.ToString();
			switch(node.Attributes["type"].Value.ToString()) {
				case "adultBook":
					this.type = MediaType.adultBook;
					break;
				case "childBook":
					this.type = MediaType.childBook;
					break;
				default:
					throw new System.Exception
							("Unexpected node '" + node.Attributes["type"].Value.ToString() + "' in book '" + node.InnerText + "'");
			}
			this.checkedOut = node.Attributes["co"].Value.Equals("True");
			if(this.checkedOut) {
				this.patronID = Int32.Parse(node.Attributes["pid"].Value);
				this.dueDate = new Date(node.Attributes["due"].Value.ToString());
			}
		}//Book(XmlNode)

		//Append book data to an XML Node   
		//Param: XML Document, XML Node to populate   
		//Post: returns XML Node reflecting the additions   
		//Note: if node already contains data, it is NOT removed. Data are only appended   
		override public XmlNode writeThyself(XmlDocument xmlDoc, XmlNode node) {
			XmlAttribute attrID = xmlDoc.CreateAttribute("id");	//Append ID
			attrID.Value = this.mediaID.ToString();
			node.Attributes.Append(attrID);
			XmlAttribute attrAuth = xmlDoc.CreateAttribute("author");	//Append Author
			attrAuth.Value = this.author;
			node.Attributes.Append(attrAuth);
			XmlAttribute attrType = xmlDoc.CreateAttribute("type");	//Append Type
			attrType.Value = this.type.ToString();
			node.Attributes.Append(attrType);
			XmlAttribute attrCO = xmlDoc.CreateAttribute("co");		//Append Checkin/out status
			attrCO.Value = this.checkedOut.ToString();
			node.Attributes.Append(attrCO);
			if(this.checkedOut) {	//Only include Duedate and Patron ID info if book is checked out
				XmlAttribute attrDue = xmlDoc.CreateAttribute("due");
				attrDue.Value = this.dueDate.ToString();
				node.Attributes.Append(attrDue);
				XmlAttribute attrPID = xmlDoc.CreateAttribute("pid");
				attrPID.Value = this.patronID.ToString();
				node.Attributes.Append(attrPID);
			}
			node.InnerText = this.title;
			return node;
		}//XmlNode writeThyself(XmlDocument, XmlNode)   
	}//class Book

	public class Media : Item {
		//Create a Media (Movie) object
		//Param:	Title, ID Code, Type of movie
		public Media(String title, String ID, MediaType type)
			: base(title, ID, type) {
			patronID = 0;//no one has checked it out yet
		}//Media(string, string, MediaType)

		//Create a Media based on data in an XML node
		//Param:	node in which to find data
		public Media(XmlNode node)
			: base() {
			this.title = node.InnerText;
			this.mediaID = node.Attributes["id"].Value.ToString();
			switch(node.Attributes["type"].Value.ToString()) {
				case "DVD":
					this.type = MediaType.DVD;
					break;
				case "Video":
					this.type = MediaType.Video;
					break;
				default:
					throw new System.Exception
							("Unexpected node '" + node.Attributes["type"].Value.ToString() + "' in movie '" + node.InnerText);
			}
			this.checkedOut = node.Attributes["co"].Value.Equals("True");
			if(this.checkedOut) {
				this.patronID = Int32.Parse(node.Attributes["pid"].Value);
				this.dueDate = new Date(node.Attributes["due"].Value.ToString());
			}
		}//Media(XmlNode)

		//Append media data to an XML Node   
		//Param: XML Document, XML Node to populate   
		//Post: returns XML Node reflecting the additions   
		//Note: if node already contains data, it is NOT removed. Data are only appended   
		override public XmlNode writeThyself(XmlDocument xmlDoc, XmlNode node) {
			XmlAttribute attrID = xmlDoc.CreateAttribute("id");		//Append ID
			attrID.Value = this.mediaID.ToString();
			node.Attributes.Append(attrID);
			XmlAttribute attrType = xmlDoc.CreateAttribute("type");	//Append type of movie
			attrType.Value = this.type.ToString();
			node.Attributes.Append(attrType);
			XmlAttribute attrCO = xmlDoc.CreateAttribute("co");	//append checkedin/out info
			attrCO.Value = this.checkedOut.ToString();
			node.Attributes.Append(attrCO);
			if(this.checkedOut) {	//Only append duedate and patron ID data if Movie is checked out
				XmlAttribute attrDue = xmlDoc.CreateAttribute("due");
				attrDue.Value = this.dueDate.ToString();
				node.Attributes.Append(attrDue);
				XmlAttribute attrPID = xmlDoc.CreateAttribute("pid");
				attrPID.Value = this.patronID.ToString();
				node.Attributes.Append(attrPID);
			}
			node.InnerText = this.title;
			return node;
		}//XmlNode writeThyself(XmlDocument, XmlNode) 
	}//class Media
}//namespace
